#include "vxsymbolsexplorer.h"

#include <math.h>
#include "vx.h"
#include "vxfont.h"
#include "vxcommandscontroller.h"
#include "vxmousetransmitter.h"


class ScrollController: public QObject
{
	Q_OBJECT
	Q_PROPERTY(QPointF _viewPoint READ _getViewPoint WRITE _setViewPoint)

public:

	ScrollController(QGraphicsView * view, int pageh, int desc) :
			_view(view), _pageh(pageh), _desc(desc), _scrollPage(false)
	{
		_commands = VxCommandsController::instance(_view);
		connect(_commands, SIGNAL(mouseMove(QPoint)), this, SLOT(_mouseMove(QPoint)));
		connect(_commands, SIGNAL(mouseWheel(int)), this, SLOT(_mouseWheel(int)));

		_animation = new QPropertyAnimation(this, "_viewPoint");
		_animation->setEasingCurve(QEasingCurve::OutQuart);
		_animation->setDuration(Vx::HandMoveDelay);
		connect(_animation, SIGNAL(valueChanged(QVariant)), this, SLOT(_valueChanged(QVariant)));
		connect(_animation, SIGNAL(finished()), this, SLOT(_scrollingFinished()));
		_view->installEventFilter(this);

		_moveTimer = new QTimer();
		_moveTimer->setInterval(300);
		connect(_moveTimer, SIGNAL(timeout()), this, SLOT(_mouseMoveTimer()));

	};


	static int ordyCalculate(QPointF center, int cellh)
	{
		int ordy = center.y()/cellh;
		if (center.y() > 0){
			ordy += 1;
		}
		return ordy;
	}

protected:
	bool eventFilter(QObject * obj, QEvent * event)
	{	if (event->type() == QEvent::Resize){
			_setViewPoint(Vx::viewPoint(_view));
		}
		return false;
	}



private:
	QGraphicsView * _view;
	QTimer * _moveTimer;
	int _pageh;
	int _desc;
	QPointF _viewPoint;
	QPoint _mousePoint;
	QPropertyAnimation * _animation;
	VxCommandsController * _commands;
	bool _scrollPage;

	qreal _opacity;

	void _setViewPoint(QPointF vp)
	{
		_viewPoint = vp;
		_view->centerOn(vp);
	}

	QPointF _getViewPoint()
	{
		return _viewPoint;
	}

	qreal _getDirection(QPoint point)
	{
		QPoint center = _view->rect().center();
		QPoint mp = _view->mapFromGlobal(point);

		int dx = mp.x() - center.x();
		int prcntx = 100*dx/center.x();


		if (abs(prcntx) < 80){
			return 0;
		}
		return (prcntx > 0) ? (prcntx - 80) : (prcntx + 80);
	}


	void _checkMousePoint()
	{
		if (_scrollPage){
			return;
		}

		qreal dir = _getDirection(_mousePoint)*10;
		if (dir == 0){
			return;
		}

		QPointF center = Vx::viewPoint(_view);

		if (_animation->state() == QAbstractAnimation::Running){
			_animation->stop();
			center = _viewPoint;
		}

		int ordy = ordyCalculate(center, _pageh);
		qreal vy =  ordy*_pageh + _desc - _pageh*0.5;

		_animation->stop();
		_animation->setStartValue(QPointF(center.x(), vy));
		_animation->setEndValue(QPointF(center.x() + dir, vy));
		_animation->start();

	}


private slots:

	void _mouseMoveTimer()
	{
		_moveTimer->stop();
		if (_commands->active()){
			_checkMousePoint();
		}
	}

	void _mouseMove(QPoint point)
	{
		_mousePoint = point;
		_moveTimer->stop();
		_moveTimer->start();
	}

	void _valueChanged(QVariant value)
	{
		if (_commands->active()){
			_checkMousePoint();
		}
	}

	void _scrollingFinished()
	{
		disconnect(_animation, SIGNAL(finished()), this, SLOT(_scrollingFinished()));
		_scrollPage = false;
		emit finishedScrolling();
	}


	void _mouseWheel(int delta)
	{

		QPointF vp = _viewPoint;

		if (_scrollPage){
			vp = _animation->endValue().toPointF();
		}

		if (delta > 0){
			vp.setY(vp.y() - _pageh);
		} else {
			vp.setY(vp.y() + _pageh);
		}

		connect(_animation, SIGNAL(finished()), this, SLOT(_scrollingFinished()));

		_scrollPage = true;

		emit beforeScrolling();

		_animation->stop();
		_animation->setStartValue(_viewPoint);
		_animation->setEndValue(vp);
		_animation->start();

	}

signals:
	void beforeScrolling();
	void finishedScrolling();

};


class ItemsZoomPreview: public QObject
{
	Q_OBJECT

public:
	ItemsZoomPreview(QGraphicsView * view, qreal zoom, int zoomdlt) :
			_view(view), _zoom(zoom), _zoomdlt(zoomdlt), _update(false)
	{
		_commands = VxCommandsController::instance(_view);
		connect(_commands, SIGNAL(mouseMove(QPoint)), this, SLOT(_mouseMove(QPoint)));
		_view->viewport()->installEventFilter(this);
		_updateTimer = new QTimer();
		_updateTimer->setInterval(300);
		connect(_updateTimer, SIGNAL(timeout()), this, SLOT(_updateItemsTimer()));
	}

protected:
	bool eventFilter(QObject * obj, QEvent * event)
	{
		switch(event->type()){
		case QEvent::Paint: {
			_updateTimer->stop();
			if (_update){
				return false;
			}
			_updateTimer->start();
			break;
		}
		default:;
		}
		return false;
	}

private:
	QGraphicsView * _view;
	qreal _zoom;
	int _zoomdlt;
	VxCommandsController * _commands;
	QPointF _mousePoint;
	bool _update;
	QTimer * _updateTimer;

	void _updateItems()
	{
		_update = true;

		emit beforeZooming();

		foreach(QGraphicsItem * item, _view->scene()->items()){
			QRectF itmbr = item->sceneBoundingRect();
			QPointF citm = itmbr.center();
			QPointF dltp = citm - _mousePoint;

			qreal dltm = dltp.manhattanLength();
			if (dltm > _zoomdlt){
				item->setScale(1);
			} else {
				if (itmbr.left() <= _mousePoint.x() && _mousePoint.x() <= itmbr.right()){
					item->setOpacity(0.5);
				} else {
				}
				dltm = dltm/_zoomdlt;

				item->setOpacity(1 - dltm*0.5);

				dltm = _zoom - (_zoom - 1)*sqrt(dltm);
				item->setScale(dltm);
			}
		}
		_update = false;
	}

private slots:

	void _mouseMove(QPoint point)
	{
		_mousePoint = _view->mapToScene(_view->mapFromGlobal(point));
		_updateItems();
	}

	void _updateItemsTimer()
	{
		_updateTimer->stop();
		if (_commands->active()){
			_updateItems();
		}
	}

signals:
	void beforeZooming();

};



VxSymbolsExplorer::VxSymbolsExplorer(QWidget * parent) :
		QFrame(parent), _ordx0(0xffffff), _ordy(0), _vscrolling(false)
{
	setObjectName("vxSymbolsExplorer");

	_createControls();
}



void VxSymbolsExplorer::_createControls()
{

	setLayout(new QHBoxLayout());

	_view = new QGraphicsView();
	_view->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

	_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

	layout()->addWidget(_view);

	QGraphicsScene * scene = new QGraphicsScene();
	_view->setScene(scene);


	VxFont * vf = new VxFont(new QFont("Verdana", 24));
	VxSymbolsGenerator * sg = new VxSymbolsGenerator();
	_symbols = new VxSymbols(vf, sg);

	QSize mxsz = _symbols->getMaxSize();

	int maxw = mxsz.width();
	int maxh = mxsz.height();

	maxw += maxw/4;
	maxh += maxh/4;

	_cellSize = QSize(maxw, maxh);

	_view->viewport()->installEventFilter(this);

	_checkCurrentChars();

	ScrollController * scrollController = new ScrollController(_view, maxh, _symbols->descent());
	connect(scrollController, SIGNAL(beforeScrolling()), this, SLOT(_beforeVScrolling()));
	connect(scrollController, SIGNAL(finishedScrolling()), this, SLOT(_finishedVScrolling()));

	VxHandController * handController = new VxHandController(_view);
	Q_UNUSED(handController);

	ItemsZoomPreview * zoomPreview = new ItemsZoomPreview(_view, 1.5, 100);
	connect(zoomPreview, SIGNAL(beforeZooming()), this, SLOT(_beforeZooming()));


}


void VxSymbolsExplorer::_beforeVScrolling()
{
	_vscrolling = true;
	_clearScene();
	_outCurrentChars(_ordx0, _ordx1, _ordy, _vscrolling);

}


void VxSymbolsExplorer::_finishedVScrolling()
{
	_vscrolling = false;

}


void VxSymbolsExplorer::_rescale()
{
	qreal h = _cellSize.height();
	qreal desc = _symbols->descent();
	_view->fitInView(0, _ordy*h + desc-h-h*0.1, 1, h*1.2, Qt::KeepAspectRatio);
}



bool VxSymbolsExplorer::eventFilter(QObject * obj, QEvent * event)
{
	switch (event->type()){
	case QEvent::Resize: {
		_rescale();
		break;
	}
	case QEvent::Paint: {
		_checkCurrentChars();
	}
	default:;
	}
	return false;
}



void VxSymbolsExplorer::_checkCurrentChars()
{
	int cellw = _cellSize.width();
	int cellh = _cellSize.height();

	qreal vieww = _view->viewport()->width();
	qreal viewh = _view->viewport()->height();

	QPointF tl = _view->mapToScene(QPoint(0,0));
	QPointF br = _view->mapToScene(QPoint(vieww, viewh));

	QPointF center = Vx::viewPoint(_view);

	int ordx0 = (tl.x() + cellw)/cellw;
	int ordx1 = (br.x() + cellw)/cellw;
	int ordy = ScrollController::ordyCalculate(center, cellh);


	if (_ordx0 == ordx0 && _ordx1 == ordx1 && _ordy == ordy){
		return;
	}

	_ordx0 = ordx0;
	_ordx1 = ordx1;
	_ordy = ordy;

	_clearScene();

	_outCurrentChars(_ordx0, _ordx1, _ordy, _vscrolling);

}


void VxSymbolsExplorer::_outCurrentChars(int ordx0, int ordx1, int ordy,  bool current)
{

	int maxw = _cellSize.width();
	int maxh = _cellSize.height();

	VxFont * vf = _symbols->font();
	VxSymbolsGenerator * sg = _symbols->generator();

	int rx = maxw*(ordx0-2);

	int dltch = 10;
	int desc = _symbols->descent();
	for (int rc=ordx0-2; rc < ordx1+2; rc++){
		int crdy = ordy;
		{

			QPainterPath * path = vf->getPath(sg->charAt(rc + dltch*crdy));
			if (path->isEmpty()){
				path = vf->getPath(sg->charAt(1));
			}

			int ry = maxh*crdy;

			QGraphicsRectItem * irect = _view->scene()->addRect(0,0,maxw, maxh+desc);
			irect->setPen(QPen(QColor(255,255,255, 255)));
			irect->setBrush(QBrush(QColor(255,255,255, 255)));
			irect->setPos(rx, ry-maxh);
			irect->setZValue(crdy-0.5);
			_items.append(irect);

			QGraphicsPathItem * ipath = _view->scene()->addPath(*path);
			ipath->setPen(QPen(QColor(0,0,0, 0)));
			ipath->setBrush(QBrush(QColor(0,0,0,255)));
			ipath->setPos(rx, ry);
			ipath->setZValue(crdy);
			ipath->setOpacity(0.5);
			_items.append(ipath);

		}
		
		
		if (current){
			crdy = ordy-1;
			{
				QPainterPath * path = vf->getPath(sg->charAt(rc + dltch*crdy));
				if (path->isEmpty()){
					path = vf->getPath(sg->charAt(1));
				}
				QGraphicsPathItem * ipath = _view->scene()->addPath(*path);
				ipath->setPen(QPen(QColor(0,0,0, 0)));
				ipath->setBrush(QBrush(QColor(0,0,0,255)));
				int ry = maxh*crdy;
				ipath->setPos(rx, ry);
				ipath->setZValue(crdy);
				ipath->setOpacity(0.5);
				_items.append(ipath);
			}
			crdy = ordy+1;
			{
				QPainterPath * path = vf->getPath(sg->charAt(rc + dltch*crdy));
				if (path->isEmpty()){
					path = vf->getPath(sg->charAt(1));
				}
				QGraphicsPathItem * ipath = _view->scene()->addPath(*path);
				ipath->setPen(QPen(QColor(0,0,0, 0)));
				ipath->setBrush(QBrush(QColor(0,0,0,255)));
				int ry = maxh*crdy;
				ipath->setPos(rx, ry);
				ipath->setOpacity(0.5);
				ipath->setZValue(crdy);
				_items.append(ipath);
			}
		}
		rx += maxw;
	}
	QRectF sr = _view->scene()->itemsBoundingRect();
	QRectF nr(sr.left()  - maxw, maxh*(ordy-3) + _symbols->descent(),
			  sr.width() + maxw*2, maxh*5);

	_view->scene()->setSceneRect(nr);

}



void VxSymbolsExplorer::_clearScene()
{
	foreach(QGraphicsItem * item, _items){
		_view->scene()->removeItem(item);
		delete item;
		_items.remove(_items.indexOf(item));
	}
}


void VxSymbolsExplorer::_beforeZooming()
{
	foreach(QGraphicsItem * item, _items){
		if (item->zValue() != _ordy && item->zValue() != _ordy-0.5){
			_view->scene()->removeItem(item);
			delete item;
			_items.remove(_items.indexOf(item));
		}
	}
}




#include "vxsymbolsexplorer.moc"

